я попытаюсь собрать здесь то, что у меня есть,
и так, как я хотел - маленькими кусочками (на инклюдах)
и с врезками.
Цифра в начале заголовка - примерный процент готовности куска

Поехали!

Основы алгоритмизации, с использованием языка С

(100%) Из истории языка


Дао породило машинный язык.
Машинный язык породил ассемблер.
Ассемблер породил компилятор.
Теперь в мире десять тысяч языков.
(Дао Программирования)


Язык С был разработан в 1972 году Кеном Томпсоном и Деннисом Ричи как язык системного программирования для системы Юникс. Позднее Юникс был переписан на С (с ассемблера), что в значительной мере предопределило распространение Юникса и самого языка. До сих пор на нем пишутся операционные системы (в частности, ядро Линукса написано на С).
Среди целей разработки языка была максимальная простота, ведущая к высокой скорости работы, малым накладным расходам и простоте реализации.

Малость языка и великость библиотеки

Сам язык достаточно мал (список ключевых слов насчитывает 32 слова). Эта особенность обеспечивается тем, что многие возможности вынесены в библиотеки. В том числе такие основные, как ввод/вывод и математические операции.
Нужно отметить, что язык С можно классифицировать как сравнительно низкоуровневый язык общего назначения. Он не содержит ряда популярных средств, имеющихся в других языках, в частности автоматического управления памятью, операций над массивами, объектно-ориентированного программирования. В стандартной библиотеке отсутствует поддержка графики и графического интерфейса пользователя.
С другой стороны, библиотека языка С достаточно велика для создания приложений.
(100%)Врезка - Стандартная библиотека С
Сам язык С – маленький. Многие часто используемые возможности вынесены в библиотеки. Библиотека является частью стандарта языка. Роль стандарта – обеспечить, чтобы ваша программа компилировалась на любом компиляторе, его поддерживающем ("переносимость").
Библиотека разделена на осмысленные части, им соответствуют заголовочные файлы. Файл нужно включить (#include) в программу, чтобы можно было этой частью пользоваться.
Всего в стандартной библиотеке около 20 заголовочных файлов, и неплохо представлять, какая смысловая группа функций где описана - на уровне "мне нужны математические функции, значит, смотрим в math.h". Справка по стандартной библиотеке часто структурирована по заголовочным файлам.
Нам будут нужны не все. Вероятно, потребуются:
  • math.h, упомянутый ранее,
  • stdio.h, функции стандартного ввода-вывода,
  • stdlib.h, ряд часто используемых функций, в т.ч. датчик случайных чисел.
  • string.h, функции работы со строками
  • conio.h, библиотека консольного ввода-вывода (в основном для реализации функциональности "нажмите любую клавишу для продолжения").
Эта библиотека не входит в стандарт, но в консольных приложениях Windows часто используется.

Язык портирован на очень многие системы и служит средством переноса прикладных программ, в свою очередь написанных на С.
В связи с этим большое значение приобретает стандартизация.
Первым неформальным стандартом языка была изданная в 1978 году книга "Язык программирования С" (Брайан Керниган, Деннис Ричи). Книга получилась хорошая и еще больше добавила языку популярности. В 1989 году был принят ANSI стандарт языка. С тех пор был принят еще один стандарт, текущий стандарт носит название С99.

(100%) Первые программы


Интересно, почему в англоязычных книгах первая программа печатает "Hello world", а в русскоязычных – "Здравствуй, дерево"?

(100%) Программа 1 - Hello world

Начнем изучать язык методом бросания себя в воду. Правда, в неглубокую – рассмотрим стандартную (со времен Кернигана и Ричи) программу "Hello world".
(100%)Врезка - О кракозябрах
Может быть, вы удивились - почему собственно "Hello world"? Почему не "Здравствуй, дерево"?
А вот попробуйте сами. Попробовали? То, что у вас получилось, неофициально называется "кракозябры". Причина явления - в различии кодировок в Windows, где вы набираете программу, и в консольном окне ("ДОС-окно"), где она потом работает. Простого способа помочь горю мне неизвестно. Можно поменять операционную систему на, скажем, Linux, где кодировка одна. Или писать транслитом: "Zdravstvuy, derevo". Или выучить английский.

Вот она:
/*Hello world program*/
#include <stdio.h>
 
int main(void)
{
    printf("\nHello, world!");
    return 0;
}
Что же можно увидеть из этого примера?

  1. Для записи программы есть определенная структура, которой надо придерживаться.
  2. Имеется возможность вводить в программу комментарии (как в первой строке). Комментарии могут быть многострочными, начинаться с /* и заканчиваться */. Все, что между ними, компилятором не рассматривается.
  3. Для подключения стандартных библиотек служит директива #include. Стандартная библиотека разделена на разделы по темам; для каждого раздела имеется заголовочный файл (header-файл) с расширением .h, перечисляющий имеющиеся в разделе функции. В данном случае, stdio.h – заголовочный файл раздела стандартного ввода вывода (STanDard Input Output).
  4. Каждая программа обязательно имеет функцию main, выполняющуюся первой. Каждая функция должна иметь тип возвращаемого значения (функция main должна иметь тип int – целое число), список параметров в скобках (в данном случае стоит слово void, указывающее на явное отсутствие параметров). После заголовка функции (тип имя параметры в скобках) идет тело функции, заключаемое в фигурные скобки.
  5. Печать выполняется функцией printf (именно для нее и подключали stdio.h)
  6. Строки заключаются в кавычки. Строки могут включать непечатные символы, например \n – символ перевода строки.
  7. Операторы разделяются точкой с запятой (;)
  8. Значение, возвращаемой функцией, указывается после оператора return.

Однако, немало. Но все что мы научились делать – это печатать строчки текста. Как насчет чего-нибудь посчитать?

(100%) Программа 2 - давайте посчитаем

Давайте заставим программу считать. Ну, "считать" это слишком гордо сказано. Пусть она считает 3+2. Заодно мы попробуем пользоваться переменными.
Итак, программа и что из нее видно:
/*3+2 program*/
#include <stdio.h>
 
int main(void)
{
    int a,b;
    int z;
    a=3;
    b=2;
    z=a+b;
    printf("\n3+2=%d",z);
    return 0;
}
Итак, что мы видим из этой программы нового?
  1. Переменные до использования надо объявлять. Иначе компилятор будет ругаться.
  2. При объявлении обязательно указывается тип переменной. У нас int обозначает целое число.
  3. Присваивание переменной значения выполняется оператором '=' (однако, есть языки, где это не так!)
  4. Для того, чтобы вывести значение на печать, используется та же функция printf, но есть нюансы (подробнее чуть позже).

Теперь пара вещей, которых не видно.
  1. Чувствительность к регистру. Язык С различает регистр символов в именах переменных и ключевых словах. Поэтому х и Х – это две разные переменные, а printf, написанный с большой буквы, – это ошибка.
  2. На самом деле, присвоение начального значения можно совместить с объявлением переменной:
    int a=3,b=2;
Обещанные нюансы о функции printf:
Когда мы видели ее в первый раз, у функции был единственный аргумент – строка. Она выводилась на печать "как есть", в неизменном виде. Теперь у нас два аргумента – строка и целая переменная. В общем случае, много параметров – строка формата и список выражений (то есть printf("\n3+2=%d",3+2); тоже можно было написать).
Строка формата используется для управления печатью. Знак % указывает, что в это место будет напечатано значение переменной. Каким именно образом, определяется следующими символами. На текущий момент нам достаточно знать, что %d это формат для целого числа (тип int).
В результате будет напечатано
    3+2=5

(80%) Программа 3 - данные надо ввести

Итак, мы смогли что-то посчитать и это что-то напечатать. Одна проблема - данные, с которыми мы работаем, жестко заданы в программе. Значит, и результаты всегда будут те же самые. Для расчета новых данных нужно исправлять, а затем перекомпилировать программу.
Такое положение дел не может нас устраивать. К счастью, мы его сейчас исправим – изучим функцию, которая позволяет вводить данные во время работы программы. Тогда одна и та же программа сможет работать на разных данных.
Для примера напишем программу-конвертор, которая будет переводить рубли в условные единицы по курсу 30 рублей за единицу.
Итак, вот программа:
/*convertor program*/
#include <stdio.h>
 
int main(void)
{
    int r, u;
    printf ("\nInput roubles amount ");
    scanf("%d", &r);
    u=r/30;
    printf("UE amount=%d",u);
    return 0;
}
Смотрим - что в программе новенького? Одна строка, вызов функции scanf. Именно эта функция осуществляет ввод с клавиатуры. Ее параметры – строка формата и список переменных, в которые будут вводиться данные. Формат мы уже видели, и помним, что это формат для целого числа. А что это за птичка перед именем переменной r?
Это операция взятия указателя.
(100%)Врезка - Об указателях
Указатель на переменную отвечает на вопрос "где лежит переменная".
Дело в том, что в языке С при вызове функции параметры передаются по значению. То есть внутри у функции появляется копия переданной переменой, с тем же значением, с которой функция может делать что хочет. Не может только одного – сделать так, чтобы изменения в копии повлияли на исходную переменную.
Поэтому-то функции scanf передается не переменная r, а указатель на нее &r. Зная указатель ("где лежит переменная"), функция сможет изменить значение r (поместить туда то, что мы ввели с клавиатуры).
Поэтому в списке scanf перед именами переменных обычно стоят птички (амперсанды, &).
Конечно, у указателей есть применения и кроме этого ;)

Ладно. Запускаем. Работает?
Пробуем ввести 45 рублей. По идее должно получиться 1.5, но печатается 1. Кто нас обманывает?
Мы сами.
Компьютер обладает забавным свойством – он делает именно то, что мы ему приказали. А не то, что мы ДУМАЛИ приказать.
Наши переменные имеют тип int (целые). При делении целых чисел в С получается целый результат:
  • 45/30 => 1
Пора изучить следующий тип данных.
(50%)Врезка - Типы данных и соответствующие ключи формата
Типы данных это не только ценный мех слова, стоящие в описании переменной. Это еще и диапазон принимаемых переменной значений, и операции, которые с этими значениями можно произвести.
Для нас это в том числе и способ ввести / вывести эти переменные. То есть, символы формата для функций printf, scanf.
Перечислим те типы, которые нам понадобятся:

Тип данных
Обозначение
Диапазон
Формат ввода/вывода
Примечания
Целое
int
-2147483648..2147483647 для 4х байтного целого
%d
зависит от реализации, обычно 4 байта
Вещественное двойной точности
double
+/- 1e307
f,e,g вывод, lf ввод
16 значащих цифр
Символ
char
0..255 или -128..127
%c
один символ
Строка
char*
это массив символов, завершенный символом с кодом 0
%s
На самом деле типа "строка" нет, это указатель на символ

У нас это будет double - вещественное число двойной точности, для краткости просто "вещественное".
Все изменения, которые потребуется внести в программу - это поменять тип int на double и поменять строку формата в функциях printf и scanf. Формат будет %lf
Проверяем. Вводим 45, получаем: 1.500000
Вводим 50, получаем: 1.666667
Цифр после запятой явно многовато. Это лечится изменением строки формата в операции печати: %.2lf (в общем виде - %w.dlf, число w – общая длина, число d – цифр после запятой).
Получаем: 1.67
Считаем задачу решенной.

(80%) Вычисления


Формула! Где формула?
если вы не видели эту рекламу, вы ничего не потеряли

Итак, чтобы писать линейные программы, вводящие данные, проводящие вычисления и выводящие результат, не хватает пустяка. А именно вычислений ;).

Операторы и приоритет

Язык С поддерживает обычные математические операторы: +, -, *, /. Вычисления выполняются по обычным математическим правилам, с учетом приоритета (то есть умножение и деление выполняются в первую очередь).
Присваиванием мы уже пользовались, так что
  • y=3+2*2
выглядит вполне понятно.
Если естественный порядок вас не устраивает, всегда можно добавить скобки:
  • y=(3+2)*2.
Кроме четырех математических операций, в программах часто встречается оператор %. В языке С это оператор взятия остатка от деления.
  • 5%2=>1, 6%2=>0, 75%10=>5
Он определен только для целых и имеет приоритет как у умножения и деления.

Сокращенная запись операторов

В языке С есть несколько уменьшающих писанину сокращений.
Во-первых, это операции инкремента и декремента (увеличение и уменьшение на 1).
  • x++ означает x=x+1,
  • x-- означает x=x-1.
Работает только для целых чисел.
(На самом деле можно записать x++, а можно ++x. В том и в том случае x увеличится. Разница проявится, только если мы собираемся использовать значение оператора в выражении.)

Во-вторых, это комбинированные операторы. Так, вместо
  • x=x+2 можно записать x+=2
Этот фокус работает со всеми операторами (и не только на целых числах).

Преобразование типов при вычислениях

В предыдущей части мы столкнулись со случаем
  • 45/30 => 1.
В общем случае, если один из элементов выражения вещественный, то и результат будет иметь вещественный тип. В противном случае, результат будет целый (как и вышло у нас).
  • double x;
  • x=1/3;
результат деления нацело – ноль, он и запишется в переменную x.
Если мы хотим получить одну третью, нужно чтобы хотя бы одна часть выражения была вещественной:
  • x=1/3.0;
Если же тип переменной, куда записывается вещественный результат, целый, то дробная часть отбрасывается. Так, для
  • int x;
  • x=1/3.0;
в результате вычисления получается вещественное число, но оно усекается до целого при присваивании. В результате – ноль.
Если результат слишком велик для представления целым числом, возникнет ошибка - переполнение.

Часто возникающие вопросы

Осталось получить ответ на несколько вопросов.
  1. Как возвести число в степень?
  2. Как записать операцию взятия квадратного корня?
  3. Где взять синус и прочие элементарные функции?

Для выполнения всех этих действий требуется библиотека math.h.
Для использования ее нужно подключить (#include <math.h>), а дальше:
1) Для возведения в степень служит функция pow

  • будет pow(2.0,3.0)
(все математические функции принимают вещественные аргументы и выдают вещественный результат).
2) Квадратный корень - функция sqrt
  • sqrt(4.0)=>2.0
3) Синус и прочие функции нужно искать там же: sin, cos, tan (это тангенс), exp, log (это натуральный логарифм)...
Нужно иметь в виду, что тригонометрические функции принимают угол не в градусах, а в радианах. Полная окружность - 360 градусов, или 2Пи радиан. Число Пи определено там же как M_PI, правда, чтобы его можно было использовать, нужно перед
  • #include <math.h>
добавить
  • #define _USE_MATH_DEFINES
Тогда, для вычисления синуса 15 градусов надо будет записать
  • y=sin(15.0*M_PI/180).

Часто спрашиваемые вопросы

В заключение - парочка часто встречающихся (на экзамене) вопросов.
1) Как посчитать

  • pow(sin(x),2.0)
2) Как получить корень 3й (7й, любой другой) степени?
  • pow(x,1/3.0)

(100%) Функции


Программа должна быть маленькой и гибкой, ее подпрограммы должны соединяться как жемчужины в ожерелье.
(Дао Программирования)

Итак, мы только что научились программировать вычисление по формуле. Любой.
Что мы будем делать, если та же формула потребуется нам еще раз? А еще?

Простой подход - копирование/вставка. Современные редакторы позволяют делать это легко.
Но этот подход - неправильный. Недостатки подхода - "разбухание" программы (один кусок повторяется много раз) и сложность внесения изменений / исправлений (нужно внести одинаковые изменения во все кусочки, которые еще надо найти. И не дай бог пропустить кусочек.)

Правильный подход - повторное использование кода. То, что будет использовано несколько раз, записывается единожды, но оформляется в виде подпрограммы. На С имеется единственный тип подпрограммы - функция. Там, где кусочек нужен, вставляется инструкция вызова функции.

Мы уже использовали функции из стандартной библиотеки. При вычислении по формуле это были знакомые из математики функции, например, синус: sin(x). Операция печати тоже выполняется функцией: printf.
Теперь научимся делать свои собственные.
Давайте создадим функцию, вычисляющую

Назовем нашу функцию f.
double f(double a, double b)
{
    double y;
    y=sin(3*a)+cos(5*b);
    return y;
}
У функции есть входные значения (параметры) - в нашем случае их два: a и b, есть возвращаемое значение (результат вычисления).
Значения имеют тип, для вычислений это в основном double.
(100%)Врезка - Void значит "пустота"
Вообще говоря, функция может не иметь параметров (например, если функция выдает случайное число; или текущее время), тогда в скобках пишется void.
Может не возвращать значения (и вместо вычислений заниматься, скажем, печатью), тогда void пишется перед названием функции.

Таким образом, первая строка - заголовок функции - будет иметь вид
double f(double a, double b)
Далее в фигурных скобках идет тело функции - "полезная нагрузка". В данном случае это вычисление выражения. Переданные параметры доступны в теле функции как переменные. Можно дополнительно объявить переменные; эти переменные будут локальными (существовать только внутри функции, никак не влияя на переменные с тем же именем снаружи / в других функциях). Мы создадим переменную y.
Полученный результат нужно вернуть как значение функции (строка "return y;").
Можно делать короче, совместив вычисление с возвратом значения. Этот пример можно сократить до одной строки:
return sin(3*a)+cos(5*b);
Теперь давайте используем нашу функцию для вычисления формулы
sin3x+cos 5y
----------------     - sin3градуса -1
cos 5(x+y)

Формула немножко хитрая (исключительно в учебных целях); если вспомнить, что sin(0)=0, а cos(0)=1, видно, что ее можно переписать с использованием нашей функции как
f(x, y)
----------- - f(3градус, 0)
f(0, x+y)

Это демонстрирует нам такое важное достоинство функций, как параметризация – что бы мы ни подставили при вызове как фактические параметры, оно при необходимости будет вычислено и передано в функцию. А там уже будет подставлено вместо формальных параметров (у нас a и b).

Для того, чтобы в основной программе (main) можно было использовать функцию, она должна быть записана выше main (можно и ниже, но тогда перед main придется вставлять заголовок функции - ее первую строку, вот так:
    double f(double a, double b);
    int main(void)
    /*код main...*/
    double f(double a, double b)
    /*код f...*/
)

Полный код программы:
/*function program*/
#define _USE_MATH_DEFINES 
#include <stdio.h>
#include <math.h>
 
double f(double a, double b)
{
    double y;
    y=sin(3*a)+cos(5*b);
    return y;
}
 
int main(void)
{
    double x=1.2, y = 2.5;        
    double z;
 
    z = f(x, y)/f(0.0, x+y) - f(3*M_PI/180, 0.0);
   printf("\nz=%lf", z);
   return 0;
}
Как мы уже выяснили раньше, строка #define _USE_MATH_DEFINES нужна для того, чтобы в math.h стала определена константа M_PI, число Пи, нужное нам для преобразование из градусов в радианы в вызове функции.

Подробнее о функциях можно почитать, например, в пособии "Информатика. Сегментация программ" Р.Н. Гурьяшова, А.В. Шеянов.

Операторы управления

(50%) Ветвление

Линейные программы это конечно хорошо, и многие вычисления отлично укладываются в эту модель. Но многие - не укладываются. Например, при решении квадратного уравнения нужно отдельно обработать случай отрицательного дискриминанта.
(70%)NaN – не число
А что, кстати, получится, если не проверить что дискриминант меньше нуля?
Попробуем:
#include <math.h>
#include <stdio.h>
 
int main(void)
{
    double x =-2, y, z;
    y=sqrt(x);
    z=2*y+x/2;
 
    printf("\n %f %f %f",x,y,z);
 
    return 0;
}
результат
-2.000000 -1.#IND00 -1.#IND00 
Мы видим, что никакого сообщения об ошибке не появилось, но в результате вычисления корня возникло "что-то странное" (y), при этом при использовании в выражении оно превращает результат в себя (распространяется по выражению) (z)!
Это "странное" означает неопределенный результат, NaN (расшифровывается как not a number – не число).
"Распространение по выражению" достаточно логично: если у нас по ходу вычисления получилось "не число", результат смысла не имеет.

Для решения подобного класса проблем есть несколько управляющих операторов, позволяющих менять путь выполнения программы. Кстати, чтобы наглядно представить путь выполнения программы, очень удобно использовать блок-схемы.

(50%) if (условный оператор)

Первый из них - условный оператор if
Переводится "если".
Пример:
if(d<0) printf("korney net");
Это простейшая форма оператора, в общем виде:
if(условие) оператор_если_да;
zz_c_04-1-if-1.gif
Оператор выполняется, только если условие истинно.
Скобки вокруг условия обязательны.

Условием на С может быть любое целочисленное выражение. 0 считается ложью, не ноль - истиной. Но, конечно, в основном применяются логические операторы.
Одно условие мы уже видели - (d<0) - ничего нового в нем нет. Аналогично, ничего непонятного нет в условиях >, >=,<=. Немного непривычно - но привыкнуть можно - что != означает "не равно". А вот проверка на равенство записывается ==, это специальный оператор, отличающийся от присваивания (=), и частый источник ошибок.
(75%)О =, == и ===
Смотрим разницу.
Правильная проверка на равенство:
if(x==y) printf("x raven y");
выполняет печать, если x равен y.

Посмотрим, что будет, если пропустить один знак равенства:
if(x=y) printf("x raven y");
Теперь в скобках стоит присваивание. В x записывается значение y, и это значение является результатом присваивания. Оператором if оно воспринимается как логическое (0 / не 0). Таким образом, печать происходит, если y не 0, что явно неверно. Кроме того, мы испортили x.

Если вы пропустите один знак равенства в своей программе, компилятор выдаст предупреждение ("возможно, неверное присваивание"), но программа запустится. Читайте предупреждения!

Программа запустится - потому что компилятор считает, что вы знаете, что делаете.
Иногда присваивание в условии - именно то, что нужно, например:
if(f=fopen("имя_файла"))
Здесь условие срабатывает, если файл успешно открылся, при этом открытый поток присваивается переменной f для дальнейшего использования.

А что же насчет ===?
Вам повезло. В языке С такой конструкции нет. ;)

Оператор if может иметь не только часть, срабатывающую по выполнению условия, но и часть, срабатывающую в противном случае:
if(условие)
    оператор_если_да;
else
    оператор_если_нет;

zz_c_04-1-if-2.gif
Имеется возможность выполнить по условию не один оператор, а несколько. Для этого операторы объединяются операторными скобками {}.

Кусок про тернарный оператор решили убрать, т.к. не встречается в лабах

(50%) switch (множественный выбор)

Итак, мы научились проверять условие, получать "да" или "нет", и в зависимости от результата выполнять нужные операторы.
Что делать, если вариантов больше двух?
В языке С имеется специальный оператор множественного выбора:
switch(mark) //оценка
{
case 5: printf("\nOtlichno"); break;
case 4: printf("\nHorosho"); break;
case 3: printf("\nTak sebe. Na samom dele - ");
case 2:
case 1: printf("\nPloho"); break;
default: printf("\nTakih ocenok ne byvaet!"); break;
}
Что мы видим из этого примера?
  • Проверяются несколько условий, но все условия вида "переменная == константа". Переменная задана в строке switch, а константы - в строках case. Проверка ведется на точное совпадение, сверху вниз.
  • Если совпадение произошло, выполняются операторы до конца блока switch - или до оператора break. Если мы не хотим, чтобы выполнение продолжалось для других меток case - ставим break.
Соответственно, в нашем примере слово "Ploho" напечатается для оценок 3, 2 и 1.
  • В конце оператора switch может стоять метка default (по умолчанию). Все случаи, не совпавшие с метками case, будут идти на default.

И что не видим:
  • На самом деле, метки оператора switch могут быть только константами, причем целочисленными (это включает в себя и одиночные символы). То есть проверять совпадение строк этим оператором нельзя.

Ограничения, накладываемые оператором switch, довольно серьезны. Так, например, функцию, заданную такой системой, запрограммировать с его помощью нельзя:
/sin(x), при x <0
y= {x, при 0<=x <1
\1, иначе

?? эту задачу можно решить с помощью оператора if ??
На самом деле, если б последняя строка была записана как
1, 1<=x
то можно было бы применить тупой (но рабочий) способ - запрограммировать каждую строчку отдельным оператом if.
if (x<0) y=sin(x);
if (0<=x && x<1) y=x;
if (1<=x) y=1;

Нетрудно видеть, что условия не пересекаются, т.е. для любого x сработает только одна строчка.
Единственная проблема в том, что у нас получилось на один if и два условия больше, чем могло бы быть.
Поэтому мы рассмотрим более красивый способ - вложенные if.
if (x<0) 
    y=sin(x)
else if (x<1) 
        y=x
    else
        y=1;

Что на самом деле присходит, легко понять с помощью блок-схемы:
<блок-схема>

Мы не проверяем 0<=x, потому что все x<0 ушли на первое условие.
У нас нет третьего if, потому что на третью строку системы попадает все, что осталось от первых двух.
Тем не менее, подобная запись имеет недостаток. Если мы имеем много вложенных if, трудно понять к какому if относится какой else.
Например:
if (x<0) 
    y=sin(x)
    if (x<1) 
        y=x
    else
        y=1;

<идея неплохая но где-то я наврал - надо посмотреть внимательно>

(??%) goto (безусловный переход)

Последний из операторов управления, который мы рассмотрим в этом разделе, это ужасный несчастный оператор goto, которым так любят пугать детей. Язык С поддерживает goto.
<пример c goto>

Уточните у вашего преподавателя, поддерживает ли goto преподаватель :)

(??%) return (оператор возврата)

(упс) не последний
return ведь тоже оператор управления, меняющий порядок выполнения программы?
Встречаясь в функции, он выполняет ее завершение и возврат в вызывающую функцию.
В случае, если он встретился в функции main, завершает работу программы.
Это можно объяснить по-разному:
  • вызывающей функции нет, возвращаться некуда;
или
  • main вызывается из операционной системы, ей и передается управление.